using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using FluentAssertions;
using Microsoft.Reactive.Testing;
using TailBlazer.Domain.FileHandling;
using TailBlazer.Domain.Infrastructure;
using Xunit;

namespace TailBlazer.Fixtures;

public class FileSearchFixture
{
    [Fact]
    public void EmptyFile()
    {
        var pulse = new Subject<Unit>();
        var scheduler = new TestScheduler();
        using (var file = new TestFile())
        {
            FileSearchResult fileSearchResult = null;

            using (file.Info
                       .Search(str => str.Contains("9"), scheduler)
                       .Subscribe(x => fileSearchResult = x))
            {
                scheduler.AdvanceByMilliSeconds(250);
                pulse.Once();
                fileSearchResult.Segments.Should().Be(1);
                fileSearchResult.SegmentsCompleted.Should().Be(0);
                fileSearchResult.IsSearching.Should().Be(false);
                fileSearchResult.Count.Should().Be(0);
            }
        }
    }

    [Fact]
    public void NotExistingFile()
    {
        var pulse = new Subject<Unit>();

        using (var file = new TestFile())
        {
            file.Delete();

            FileSearchResult fileSearchResult = null;

            using (file.Info.WatchFile(pulse)
                       .Search(str => str.Contains("9"))
                       .Subscribe(x => fileSearchResult = x))
            {
                fileSearchResult.Should().BeNull();
            }
        }
    }

    //[Fact]
    //Cannot recreate the file as something is hanging on to it.
    public void CreateFileLater()
    {
        var pulse = new Subject<Unit>();

        using (var file = new TestFile())
        {
            file.Delete();

            FileSearchResult fileSearchResult = null;

            using (file.Info.WatchFile(pulse)
                       .Search(str => str.Contains("9"))
                       .Subscribe(x => fileSearchResult = x))
            {
                fileSearchResult.Should().Be(FileSearchResult.None);
                file.Create();
                pulse.Once();
                fileSearchResult.Should().NotBe(FileSearchResult.None);
            }
        }
    }

    [Fact]
    public void SearchOnDataWhenFileIsPopulated()
    {
        var pulse = new Subject<Unit>();

        using (var file = new TestFile())
        {
            FileSearchResult fileSearchResult = null;


            using (file.Info.WatchFile(pulse)
                       .Search(str => str.Contains("9"))
                       .Subscribe(x => fileSearchResult = x))
            {
                file.Append(Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray());
                pulse.Once();
                pulse.Once();
                fileSearchResult.Matches.Length.Should().NotBe(0);
            }
        }
    }
    [Fact]
    public void InitiallyEmptyThenLinesAdded()
    {
        var scheduler = new TestScheduler();

        using (var file = new TestFile())
        {
            FileSearchResult fileSearchResult = null;
                
            using (file.Info.WatchFile(scheduler:scheduler)
                       .Search(str => str.Contains("9"))
                       .Subscribe(x => fileSearchResult = x))
            {
                scheduler.AdvanceBy(1);
                file.Append(Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray());
                scheduler.AdvanceByMilliSeconds(250);
                fileSearchResult.Matches.Length.Should().Be(19);
            }
        }
    }

    [Fact]
    public void WillContinuallyTail()
    {
        var scheduler = new TestScheduler();
        var pulse = new Subject<Unit>();
            
        using (var file = new TestFile())
        {
            FileSearchResult fileSearchResult = null;
            file.Append(Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray());

            using (file.Info.WatchFile(pulse)
                       .Search(str => str.Contains("9"), scheduler)
                       .Subscribe(x => fileSearchResult = x))
            {
                pulse.Once();
                fileSearchResult.Matches.Length.Should().Be(19);

                file.Append(new [] {"9","20"});
                pulse.Once();
                fileSearchResult.Matches.Length.Should().Be(20);

                file.Append(new[] { "9999" });
                pulse.Once();
                fileSearchResult.Matches.Length.Should().Be(21);
            }
        }
    }

    public void CanReadLinesBack()
    {
        var scheduler = new TestScheduler();
        var pulse = new Subject<Unit>();

        using (var file = new TestFile())
        {
            FileSearchResult fileSearchResult = null;
            file.Append(Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray());

            using (file.Info.WatchFile(pulse)
                       .Search(str => str.Contains("9"), scheduler)
                       .Subscribe(x => fileSearchResult = x))
            {
                pulse.Once();
                fileSearchResult.Matches.Length.Should().Be(19);

                file.Append(new[] { "9", "20" });
                pulse.Once();
                fileSearchResult.Matches.Length.Should().Be(20);

                file.Append(new[] { "9999" });
                pulse.Once();
                fileSearchResult.Matches.Length.Should().Be(21);
            }
        }
    }

    [Fact]
    public void CanReadLinesBackInLargeFile()
    {
        var scheduler = new TestScheduler();
        var pulse = new Subject<Unit>();

        using (var file = new TestFile())
        {
            IEnumerable<Line> lines = null;
            file.Append(Enumerable.Range(1, 100000).Select(i => i.ToString()).ToArray());

            using (file.Info.WatchFile(pulse)
                       .Search(str => str.Contains("9"), scheduler)
                       .Select(result => file.Info.ReadLinesByPosition(result.Matches).ToArray())
                       .Subscribe(x => lines=x))
            {
                pulse.Once();
                // fileSearchResult.Matches.Length.Should().Be(19);

                file.Append(new[] { "9", "20" });
                pulse.Once();
                //  fileSearchResult.Matches.Length.Should().Be(20);

                file.Append(new[] { "9999" });
                pulse.Once();
                //   fileSearchResult.Matches.Length.Should().Be(21);
            }
        }
    }
}